﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BassDrop.Core.Common.Cloud.Assets;
using BassDrop.Core.Common.Data.IO.Portable;
using BassDrop.Core.Common.Exceptions.Cloud;
using BassDrop.Core.Common.Localization;
using BassDrop.Core.Common.Plugins;

namespace BassDrop.Core.Common.Cloud
{
	/// <summary>
	/// A Proxy that provides access to the currently default cloud provider.
	/// It is a singleton class.
	/// </summary>
	public class DefaultCloudConnectionProxy : CloudConnectionBase
	{
		private static DefaultCloudConnectionProxy s_instance = new DefaultCloudConnectionProxy();

		/// <summary>
		/// Returns the singleton instance of the class
		/// </summary>
		public static DefaultCloudConnectionProxy Instance
		{
			get
			{
				return s_instance;
			}
		}

		/// <summary>
		/// The descriptor object of the plugin owning this object
		/// </summary>
		public override BassDropPluginBase PluginDescriptor
		{
			get
			{
				var defaultProvider = Controller.Instance.PluginManager.GetDefaultCloudProvider();

				if ( defaultProvider != null )
				{
					return defaultProvider.PluginDescriptor;
				}
				else
				{
					throw new CloudUnavailableException( Strings.ExceptionDefaultCloudUnavailable, null, HttpStatusCode.ServiceUnavailable );
				}
			}
		}

		/// <summary>
		/// The name of this plugin instance
		/// </summary>
		public override string InstanceName
		{
			get
			{
				var defaultProvider = Controller.Instance.PluginManager.GetDefaultCloudProvider();

				if ( defaultProvider != null )
				{
					return defaultProvider.InstanceName;
				}
				else
				{
					throw new CloudUnavailableException( Strings.ExceptionDefaultCloudUnavailable, null, HttpStatusCode.ServiceUnavailable );
				}
			}
		}

		/// <summary>
		/// The options of this plugin instance
		/// </summary>
		public override PluginOptionsSetBase Options
		{
			get
			{
				var defaultProvider = Controller.Instance.PluginManager.GetDefaultCloudProvider();

				if ( defaultProvider != null )
				{
					return defaultProvider.Options;
				}
				else
				{
					throw new CloudUnavailableException( Strings.ExceptionDefaultCloudUnavailable, null, HttpStatusCode.ServiceUnavailable );
				}
			}
		}

		/// <summary>
		/// The ID of this client
		/// </summary>
		public override string ClientID
		{
			get
			{
				var defaultProvider = Controller.Instance.PluginManager.GetDefaultCloudProvider();

				if ( defaultProvider != null )
				{
					return defaultProvider.ClientID;
				}
				else
				{
					throw new CloudUnavailableException( Strings.ExceptionDefaultCloudUnavailable, null, HttpStatusCode.ServiceUnavailable );
				}
			}
		}

		/// <summary>
		/// The vector clocks in the system
		/// </summary>
		public override VectorClock Clocks
		{
			get
			{
				var defaultProvider = Controller.Instance.PluginManager.GetDefaultCloudProvider();

				if ( defaultProvider != null )
				{
					return defaultProvider.Clocks;
				}
				else
				{
					throw new CloudUnavailableException( Strings.ExceptionDefaultCloudUnavailable, null, HttpStatusCode.ServiceUnavailable );
				}
			}
		}

		/// <summary>
		/// Gets if this cloud provider is the default one
		/// </summary>
		public override bool IsDefault
		{
			get
			{
				var defaultProvider = Controller.Instance.PluginManager.GetDefaultCloudProvider();

				if ( defaultProvider != null )
				{
					return true;
				}
				else
				{
					throw new CloudUnavailableException( Strings.ExceptionDefaultCloudUnavailable, null, HttpStatusCode.ServiceUnavailable );
				}
			}
		}

		/// <summary>
		/// Returns if the provider is ready to serve requests
		/// </summary>
		public override bool IsAvailable
		{
			get
			{
				var defaultProvider = Controller.Instance.PluginManager.GetDefaultCloudProvider();

				return defaultProvider != null && defaultProvider.IsAvailable;
			}
		}

		/// <summary>
		/// Creates a new cloud proxy
		/// </summary>
		private DefaultCloudConnectionProxy()
			: base( null, null )
		{
		}

		/// <summary>
		/// Returns the account information for this cloud connection (name, quotas, etc.)
		/// </summary>
		/// <returns></returns>
		public override async Task<AccountInfo> GetAccountInfo()
		{
			var defaultProvider = Controller.Instance.PluginManager.GetDefaultCloudProvider();

			if ( defaultProvider != null )
			{
				return await defaultProvider.GetAccountInfo();
			}
			else
			{
				throw new CloudUnavailableException( Strings.ExceptionDefaultCloudUnavailable, null, HttpStatusCode.ServiceUnavailable );
			}
		}

		/// <summary>
		/// Makes this cloud provider the default one
		/// </summary>
		public override void MakeDefault()
		{
			// already the default
		}

		/// <summary>
		/// Unsets this cloud provider from being the default one
		/// </summary>
		protected override void UnmakeDefault()
		{
			// not needed, the default provider does this on its own
		}

		/// <summary>
		/// Copies a file from the cloud to the local filesystem.
		/// Overwrites any existing files.
		/// </summary>
		/// <param name="remotePath">path to the file in the cloud</param>
		/// <param name="localPath">path to the file in the local filesystem</param>
		protected override async Task DoGet( string remotePath, Uri localPath )
		{
			var defaultProvider = Controller.Instance.PluginManager.GetDefaultCloudProvider();

			if ( defaultProvider != null )
			{
				await DoGet( defaultProvider, remotePath, localPath );
			}
			else
			{
				throw new CloudUnavailableException( Strings.ExceptionDefaultCloudUnavailable, null, HttpStatusCode.ServiceUnavailable );
			}
		}

		/// <summary>
		/// Copies a file from the local filesystem to the cloud.
		/// Overwrites any existing files.
		/// </summary>
		/// <param name="remotePath">path to the file in the cloud</param>
		/// <param name="localPath">path to the file in the local filesystem</param>
		protected override async Task DoPut( string remotePath, Uri localPath )
		{
			var defaultProvider = Controller.Instance.PluginManager.GetDefaultCloudProvider();

			if ( defaultProvider != null )
			{
				await DoPut( defaultProvider, remotePath, localPath );
			}
			else
			{
				throw new CloudUnavailableException( Strings.ExceptionDefaultCloudUnavailable, null, HttpStatusCode.ServiceUnavailable );
			}
		}

		/// <summary>
		/// Checks if a file exists in the cloud.
		/// </summary>
		/// <param name="remotePath">path to the file in the cloud</param>
		/// <returns>does the file exist</returns>
		protected override async Task<bool> DoExists( string remotePath )
		{
			var defaultProvider = Controller.Instance.PluginManager.GetDefaultCloudProvider();

			if ( defaultProvider != null )
			{
				return await DoExists( defaultProvider, remotePath );
			}
			else
			{
				throw new CloudUnavailableException( Strings.ExceptionDefaultCloudUnavailable, null, HttpStatusCode.ServiceUnavailable );
			}
		}

		/// <summary>
		/// Returns a list of file paths matching a regular expression from baseDir.
		/// </summary>
		/// <param name="baseDir">path to the base directory of the search</param>
		/// <param name="regex">regular expression to match</param>
		/// <param name="recursive">true to search subfolders</param>
		/// <returns></returns>
		protected override async Task<List<string>> DoFind( string baseDir, Regex regex, bool recursive )
		{
			var defaultProvider = Controller.Instance.PluginManager.GetDefaultCloudProvider();

			if ( defaultProvider != null )
			{
				return await DoFind( defaultProvider, baseDir, regex, recursive );
			}
			else
			{
				throw new CloudUnavailableException( Strings.ExceptionDefaultCloudUnavailable, null, HttpStatusCode.ServiceUnavailable );
			}
		}

		/// <summary>
		/// Returns the metadata of the passed file
		/// </summary>
		/// <param name="path"></param>
		/// <returns></returns>
		protected override async Task<FileMetadata> DoGetMetadata( string path )
		{
			var defaultProvider = Controller.Instance.PluginManager.GetDefaultCloudProvider();

			if ( defaultProvider != null )
			{
				return await DoGetMetadata( defaultProvider, path );
			}
			else
			{
				throw new CloudUnavailableException( Strings.ExceptionDefaultCloudUnavailable, null, HttpStatusCode.ServiceUnavailable );
			}
		}

		/// <summary>
		/// Moves/renames a file inside the cloud. Overwriting existing files is not allowed.
		/// </summary>
		/// <param name="oldPath">the old path to the file in the cloud</param>
		/// <param name="newPath">the new (desired) path to the file in the cloud</param>
		protected override async Task DoMove( string oldPath, string newPath )
		{
			var defaultProvider = Controller.Instance.PluginManager.GetDefaultCloudProvider();

			if ( defaultProvider != null )
			{
				await DoMove( defaultProvider, oldPath, newPath );
			}
			else
			{
				throw new CloudUnavailableException( Strings.ExceptionDefaultCloudUnavailable, null, HttpStatusCode.ServiceUnavailable );
			}
		}

		/// <summary>
		/// Deletes a file from the cloud if it exists. Blocks until a lock is acquired for it.
		/// </summary>
		/// <param name="remotePath">path to the file in the cloud</param>
		protected override async Task DoDelete( string remotePath )
		{
			var defaultProvider = Controller.Instance.PluginManager.GetDefaultCloudProvider();

			if ( defaultProvider != null )
			{
				await DoDelete( defaultProvider, remotePath );
			}
			else
			{
				throw new CloudUnavailableException( Strings.ExceptionDefaultCloudUnavailable, null, HttpStatusCode.ServiceUnavailable );
			}
		}
	}
}
